package cn.feng.process.config.basic;

import cn.feng.process.config.basic.base.ServiceException;
import cn.feng.process.config.basic.generate.JavaStringCompiler;
import cn.feng.process.core.IProcessFactory;
import cn.feng.process.core.ProcessConstant;
import cn.feng.process.core.SpringUtil;
import cn.feng.process.core.enums.ProcessConfigTypeEnum;
import cn.feng.process.core.pojo.DO.ProcessDefine;
import cn.feng.process.core.pojo.DO.ProcessNode;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 流程配置类
 *
 * @author zzf
 * @date 2020/9/14 10:17
 */
@Configuration
public class ProcessConfig {

    @Autowired
    private ProcessConfigProperty property;

    public static ProcessHandler I;

    @PostConstruct
    public void init() {
        I = new ProcessHandler(property);
    }

    /**
     * 流程配置参数类
     *
     * @author zzf
     */
    @Setter
    @Getter
    @Component
    @ConfigurationProperties(prefix = "process.config")
    public static class ProcessConfigProperty {

        /**
         * 流程配置方式
         */
        private ProcessConfigTypeEnum type;

        /**
         * 用户表实体类
         */
        private String userClassName;

        /**
         * 用户表实体类
         */
        private String userServiceClassName;

        /**
         * 流程定义集合
         */
        @NestedConfigurationProperty
        private List<ProcessDefine> processDefineList;

    }


    /**
     * 流程处理类
     * <p>
     * 在初始化时，需要初始化其成员属性。提供后续发起流程、创建流程时调用。
     */
    public static class ProcessHandler {

        private ProcessConfigProperty property;

        private List<ProcessDefine> processDefineList;//流程定义集合

        private IProcessFactory processFactory;//流程工厂

        private JavaStringCompiler compiler = new JavaStringCompiler();//代码生成器

        private Map<String, byte[]> classMap = new HashMap<>();//工厂类代码字节数组

        private Map<String, ProcessDefine> processDefineMap = new HashMap<>(8);

        private Class<?> userClass;

        private Class<?> userServiceClass;

        private Object userServiceBean;

        private Method getUserMapMethod;

        private Method listUserMapMethod;

        private String userTableName;

        private String userIdField;

        /* 常量定义 begin */
        //流程工厂名
        private String FACTORY_CLASS_NAME = "ProcessFactory";
        //创建的类的父路径
        private String CLASS_BASE_PATH = "cn.feng.process.config.basic";
        /* 常量定义 end */

        private final String endChar = ";";
        private final String indent = "    ";
        private final String override = "@Override";

        public ProcessHandler(ProcessConfigProperty property) {
            this.property = property;
            processDefineList = property.getProcessDefineList();
            if(true) {
                return;
            }
            try {
                userClass = Class.forName(property.getUserClassName());
                userServiceBean = SpringUtil.getBean(Class.forName(property.getUserServiceClassName()));
                getUserMapMethod = userServiceClass.getMethod("getMap", Wrapper.class);
                listUserMapMethod = userServiceClass.getMethod("listMaps", Wrapper.class);


                TableName annotation = userClass.getAnnotation(TableName.class);
                if (annotation != null) {
                    userTableName = annotation.value();
                } else {
                    throw new ServiceException("指定用户对象未声明对应数据库表名！");
                }
                //插入map数据
                processDefineList.forEach(s -> processDefineMap.put(s.getTargetClassName(), s));



        /*        String classStr = loadFactoryClassFileString();

                generateProcessFile();

                Class<?> aClass = compiler.loadClass(CLASS_BASE_PATH + "." + FACTORY_CLASS_NAME, classMap);
                Object o = aClass.newInstance();
                processFactory = (IProcessFactory) o;*/
            } catch (Exception e) {
                e.printStackTrace();
                throw new ServiceException("流程配置初始化失败！");
            }
        }


        public ProcessDefine getProcessDefine(String targetName) {
            return processDefineMap.get(targetName);
        }

        private JSONObject getUserMap(long userId) {
            QueryWrapper wrapper = new QueryWrapper();
            wrapper.eq(userIdField, userId);
            try {
                Object result = getUserMapMethod.invoke(userServiceBean, wrapper);
                if (!ObjectUtils.isEmpty(result)) {
                    return JSON.parseObject(JSON.toJSONString(result));
                } else {
                    throw new ServiceException("查询用户信息失败！");
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new ServiceException("查询用户信息失败！");
            }
        }


        /**
         * 生成对应流程类
         */
        private void generateProcessFile() {

            List<ProcessDefine> defineList = property.getProcessDefineList();

            for (ProcessDefine defile : defineList) {
                String targetClassName = defile.getTargetClassName();


            }

        }

        private String loadFactoryClassFileString() throws ClassNotFoundException {

            StringBuilder builder = new StringBuilder("package ").append(CLASS_BASE_PATH).append(endChar).append(System.lineSeparator());

            /* 添加import信息 begin */
            builder.append("import cn.feng.process.core.IProcessFactory;");
            //用户类
            Class<?> userClass = Class.forName(property.getUserClassName());
            builder.append("import ").append(userClass.getPackage()).append(userClass.getCanonicalName()).append(endChar);

            //用户业务类
            Class<?> userServiceClass = Class.forName(property.getUserServiceClassName());
            builder.append("import ").append(userServiceClass.getPackage()).append(userServiceClass.getCanonicalName()).append(endChar);

            List<ProcessDefine> defineList = property.getProcessDefineList();
            for (ProcessDefine s : defineList) {
                //对象类
                Class<?> targetClass = Class.forName(s.getTargetClassName());
                builder.append("import ").append(targetClass.getPackage()).append(targetClass.getCanonicalName()).append(endChar);
                //对象业务类
                Class<?> targetServiceClass = Class.forName(s.getTargetServiceClassName());
                builder.append("import ").append(targetServiceClass.getPackage()).append(targetServiceClass.getCanonicalName()).append(endChar);
            }
            /* 添加import信息 end */

            builder.append("public class ").append(FACTORY_CLASS_NAME).append("implements IProcessFactory {");

            /* 加载成员属性 begin */
            builder.append(indent).append("Class<?> userClass;");
            builder.append(indent).append("Class<?> userServiceClass;");
            for (int i = 0; i < defineList.size(); i++) {
                builder.append(indent).append("Class<?> targetClass").append(i).append(endChar);
                builder.append(indent).append("Class<?> targetServiceClass").append(i).append(endChar);
            }
            /* 加载成员属性 end */

            /* 重写IProcessFactory方法 begin */
            //setUserClass
            builder.append(indent).append(override).append(System.lineSeparator());
            builder.append(indent).append("public void setUserClazz(Clazz<?> clazz) {").append(System.lineSeparator());
            builder.append(indent).append(indent).append("this.userClass = clazz;");
            builder.append(indent).append("}");
            //setUserServiceClass
            builder.append(indent).append(override).append(System.lineSeparator());
            builder.append(indent).append("public void setUserServiceClass(Clazz<?> clazz) {").append(System.lineSeparator());
            builder.append(indent).append(indent).append("this.userServiceClass = clazz;");
            builder.append(indent).append("}");
            //setTargetClass
            builder.append(indent).append(override).append(System.lineSeparator());
            builder.append(indent).append("public void setTargetClass(Clazz<?> clazz) {").append(System.lineSeparator());
            builder.append(indent).append(indent).append("this.targetClass = clazz;");
            builder.append(indent).append("}");
            //setTargetServiceClass
            builder.append(indent).append(override).append(System.lineSeparator());
            builder.append(indent).append("public void setTargetServiceClass(Clazz<?> clazz) {").append(System.lineSeparator());
            builder.append(indent).append(indent).append("this.targetServiceClass = clazz;");
            builder.append(indent).append("}");
            //getUserClass
            builder.append(indent).append(override).append(System.lineSeparator());
            builder.append(indent).append("public Class<?> getUserClass() {").append(System.lineSeparator());
            builder.append(indent).append(indent).append("return this.userClass;");
            builder.append(indent).append("}");
            //getUserServiceClass
            builder.append(indent).append(override).append(System.lineSeparator());
            builder.append(indent).append("public Class<?> getUserServiceClass() {").append(System.lineSeparator());
            builder.append(indent).append(indent).append("return this.userServiceClass;");
            builder.append(indent).append("}");
            //getTargetClass
            builder.append(indent).append(override).append(System.lineSeparator());
            builder.append(indent).append("public Class<?> getTargetClass() {").append(System.lineSeparator());
            builder.append(indent).append(indent).append("return this.targetClass;");
            builder.append(indent).append("}");
            //getTargetServiceClass
            builder.append(indent).append(override).append(System.lineSeparator());
            builder.append(indent).append("public Class<?> getTargetServiceClass() {").append(System.lineSeparator());
            builder.append(indent).append(indent).append("return this.targetServiceClass;");
            builder.append(indent).append("}");
            //getProcess(Class<?> clazz)
            builder.append(indent).append(override).append(System.lineSeparator());
            builder.append(indent).append("public BaseProcess getProcess(Class<?> clazz) {").append(System.lineSeparator());
            builder.append(indent).append(indent);
            builder.append(indent).append("}");
            //getProcess(String className)
            builder.append(indent).append(override).append(System.lineSeparator());
            builder.append(indent).append("public BaseProcess getProcess(String className) {").append(System.lineSeparator());
            builder.append(indent).append(indent);
            builder.append(indent).append("}");


            /* 重写IProcessFactory方法 end */
            builder.append("}");


            return builder.toString();
        }

        /**
         * 获取流程执行人ID集合
         *
         * @param initiatorUserId 流程发起人ID
         * @param node            流程节点对象
         */
        public List<Long> getProcessExecuteUserIdList(long initiatorUserId, ProcessNode node) {
            List<Long> userIdList = new ArrayList<>();
            switch (node.getOperationTargetType()) {
                case ProcessConstant.OPERATION_TARGET_TYPE_USER_FIELD: {
                    QueryWrapper wrapper = new QueryWrapper();
                    wrapper.select(userIdField);
                    String targetVal = node.getTargetVal();
                    if (targetVal.contains("[") && targetVal.contains("]")) {
                        wrapper.in(node.getTargetField(), JSONObject.parseArray(targetVal));
                    } else {
                        wrapper.eq(node.getTargetField(), JSONObject.parse(targetVal));
                    }

                    break;
                }
                case ProcessConstant.OPERATION_TARGET_TYPE_TARGET_FIELD: {
                    try {
                    } catch (Exception e) {
                        throw new ServiceException("发起流程失败：获取对象信息失败！");
                    }

                    break;

                }
                case ProcessConstant.OPERATION_TARGET_TYPE_TRIGGER_EVENT: {

                }
            }
            return null;
        }


        public List<ProcessDefine> getProcessDefineList() {
            return property.processDefineList;
        }
    }
}
